#include "log.h"
Log::Log()
{   
    countLine_ = 0;
    level_ = 0;
    maxLine_ = 0;
    isOpen_ = false;
    isAsync_ = false;
    fp=nullptr;
    deque_ = nullptr;
}
Log::~Log()
{
    if(deque_ != nullptr)
    {
        while(!deque_->empty())
        {
            deque_->wake();
        }
        deque_->close();
        WriteThread_->join();
        if(fp)
        {
            std::lock_guard<std::mutex> mtx(mutex_);
            wake();
            fclose(fp);
        }
    }
}
void Log::wakeWriteThread()
{
    Log::getInstance()->AsyncWriteLog();
}
void Log::init(int level,const char*path,const char* suffix,bool isasync)
{
    isOpen_ = true;
    level_  =level;
    path_ = path;
    suffix_ = suffix;
    isAsync_ = isasync;
    if(isAsync_ == true)//异步
    {
        if(deque_ == nullptr)
        {
            deque_.reset(new BlockQueue<std::string>());
            WriteThread_.reset(new std::thread(wakeWriteThread));
        }
    }
    //初始化 toDay_、 FileName
    //FileName: ./Logs/年-月-日.suffix_
    countLine_ = 0;
    time_t now_time = time(0);
    struct tm* tm_ = localtime(&now_time);
    char FileName[LOG_NAME_LEN];
    snprintf(FileName,sizeof(FileName)-1,
            "%s/%04d_%02d_%02d%s",path_,tm_->tm_year+1900,tm_->tm_mon+1,tm_->tm_mday,suffix_);
    toDay_ = tm_->tm_mday;


    /*这里的判断可以修改的更简单一点 使用 stat结构中的判断 或者 access*/
    //开始初始化 fp
    std::lock_guard<std::mutex> mtx(mutex_);
    //初始化/清空buff_
    buff_.makezero();
    if(fp!=nullptr)
    {
        wake();
        fclose(fp);
    }
    //文件夹路径 + 文件名
    std::string PATH = std::move(std::string(FileName,strlen(FileName)));
    //先查看是否有 今天为日期 的文件 
    //查看当前文件夹有没有 paht_目录
    int tfd = access(path_,F_OK);
    if(tfd == -1) 
    {
        std::cout<<"没有日志目录: "<<path_<<" 开始创建..."<<std::endl;
        mkdir(path_,0777);
    }
    //查看目录 path_ 下有没有今天的日志
    tfd = access(FileName,F_OK);
    if(tfd == -1) 
    {  
        std::cout<<"没有日志: "<<FileName<<" 开始创建..."<<std::endl;
        int fd = open(FileName,O_WRONLY|O_CREAT,S_FILE);
        close(fd);
    }
    //目录 、 文件都存在
    fp = fopen(FileName,"a");
    if(fp==nullptr){ perror("fopen"); }
    assert(fp!=nullptr);

/*  std::lock_guard<std::mutex> mtx(mutex_);
    buff_.makezero();
    if(fp != nullptr)
    {
        wake();
        fclose(fp);
    }
    fp = fopen(FileName,"a");
    if(fp == nullptr)
    {
        mkdir(path_,0777);
        fp = fopen(FileName,"a");
    }
    assert(fp != nullptr);
*/    
}
Log* Log::getInstance()
{
    static Log log;
    return &log;
}
void Log::write(int level,const char * format,...)
{
    timeval start;
    gettimeofday(&start,0);
    time_t now = start.tv_sec;
    struct tm *temp = localtime(&now);
    struct tm tm_ = *temp;
    va_list list;

    //根据现在的时间(day)/行數判断是否要再新建新的 Logs
    if((toDay_ != tm_.tm_mday) || (countLine_ && ( countLine_ % MAX_LINE== 0) ))
    {
        //std::cout<<"切换文件"<<std::endl;
        std::unique_lock<std::mutex> mtx(mutex_);
        mtx.unlock();
        char FileName[LOG_NAME_LEN];bzero(FileName,sizeof(FileName)/sizeof(FileName[0]));
        char tali[36];bzero(tali,sizeof(tali)/sizeof(tali[0]));
        snprintf(tali,sizeof(tali),
                "%04d_%02d_%02d",tm_.tm_year+1900,tm_.tm_mon+1,tm_.tm_mday);

        //若时间不对
        if(toDay_ != tm_.tm_mday)
        {
            snprintf(FileName,LOG_NAME_LEN/2,"%s/%s%s",path_,tali,suffix_);
            toDay_ = tm_.tm_mday;
            countLine_ = 0;
            
        }
        //若行数超过
        else
        {
            snprintf(FileName,LOG_NAME_LEN/2,"%s/%s%d%s",
                                            path_,tali,(countLine_/MAX_LINE),suffix_);
        }
        wake();
        fclose(fp);
        fp =fopen(FileName,"a");
        if(fp ==nullptr) {perror("fopen");}
        mtx.lock();
        assert(fp!=nullptr);

    }
    assert(fp!=nullptr);

    //向阻塞队列中写入 信息
    //需要 1.时间 2.[level] 3.Args
    {
        std::unique_lock<std::mutex> mtx(mutex_);
        countLine_++;
        int len=0;
        len = snprintf(buff_.WIndex_ptr(),128,"%04d-%02d-%02d %02d-%02d-%02d.%06ld",
                tm_.tm_year+1900,tm_.tm_mon+1,tm_.tm_mday,
                tm_.tm_hour,tm_.tm_min,tm_.tm_sec,start.tv_usec);

        buff_.HasWriten(len);
        
        addLevelTile(level);
        
        va_start(list,format); 
        len = vsnprintf(buff_.WIndex_ptr(),buff_.writeableBytes(),format,list);
        va_end(list);
        buff_.HasWriten(len);
        buff_.append("\n\0",2);
        
        if((isAsync_ ==true) && (deque_!=nullptr) &&(!deque_->full()))
        {
            std::string msg=std::move(buff_.getReadBlock());
            deque_->push_back(msg);
        }
        else
        {
            fputs(buff_.RIndex_ptr(),fp);
        }
        //清空buff_
        buff_.makezero();
    }
}
void Log::wake()
{
    if(isAsync_ == true)
    {
        deque_->wake();
    }
    std::fflush(fp);
}
int Log::getlevel()
{
    std::lock_guard<std::mutex> mtx(mutex_);
    return level_;
}
void Log::setlevel(int level)
{
    std::lock_guard<std::mutex> mtx(mutex_);
    level_ = level;
}
void Log::addLevelTile(int level)
{
    switch(level)
    {
        case 0:
            buff_.append("[debug]: ",9);
            break;
        case 1:
            buff_.append("[info] : ",9);
            break;
        case 2:
            buff_.append("[warn] : ",9);
            break;
        case 3:
            buff_.append("[errno]: ",9);
            break;
        default:
            buff_.append("[info] : ",9);
            break;
    }
}
void Log::AsyncWriteLog()
{
    std::string str="";
    while(deque_->pop(str))
    {
        std::lock_guard<std::mutex> mtx(mutex_);
        fputs(str.c_str(),fp);
    }
}
//void threattest()
//{
//    LOG_INFO("123456789,safdafafasfaf");
//}
//int main()
//{
//    Log::getInstance()->init(0,"./Logs",".log",1);
//    std::cout<<"写入完成 ???"<<std::endl;
//    for(int i = 0 ;i<400;i++)
//    {
//        //sleep(1);
//        threattest();
//        std::cout<<((i/400.0)*100)<<"%"<<std::endl;
//    }
//    return 0;
//}